Skip to content

Fix terminal history, focus recovery, and stale PTY events#6354

Merged
AmethystLiang merged 8 commits into
mainfrom
wire-terminal-worktree-navitgation-to-back-and-forth-stack
Jun 29, 2026
Merged

Fix terminal history, focus recovery, and stale PTY events#6354
AmethystLiang merged 8 commits into
mainfrom
wire-terminal-worktree-navitgation-to-back-and-forth-stack

Conversation

@AmethystLiang

Copy link
Copy Markdown
Contributor

Summary

Describe the user-visible change.

Screenshots

  • Add screenshots or a screen recording for any new or changed UI behavior.
  • If there is no visual change, say No visual change.

Testing

  • pnpm lint
  • pnpm typecheck
  • pnpm test
  • pnpm build
  • Added or updated high-quality tests that would catch regressions, or explained why tests were not needed

AI Review Report

Summarize the code review you ran with your AI coding agent. Include the main risks it checked, what it flagged, and what you changed or verified as a result.
Confirm that the review explicitly checked cross-platform compatibility for macOS, Linux, and Windows, including shortcuts, labels, paths, shell behavior, and any Electron-specific platform differences touched by this PR.

Security Audit

Provide a basic security audit summary from your AI coding agent. Call out any input handling, command execution, path handling, auth, secrets, dependency, or IPC risks that were reviewed, plus any follow-up needed.

Notes

Call out any platform-specific behavior, risks, or follow-up work.

@AmethystLiang AmethystLiang force-pushed the wire-terminal-worktree-navitgation-to-back-and-forth-stack branch from 282a0c8 to 2bc8e16 Compare June 25, 2026 11:16
@AmethystLiang AmethystLiang changed the title Wire terminal worktree navitgation to back and forth stack Fix terminal history, focus recovery, and stale PTY events Jun 25, 2026
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR updates terminal layout normalization, active leaf resolution, hydration, shutdown capture, pane binding repair, PTY exit handling, remote PTY input batching, IPC focus wiring, and visible-terminal wake recovery. It adds logic to prefer PTY-backed active leaves, repair stale selection state, clear queued input on handle changes, and recover visible terminal state on window focus or visibility changes. Tests were added and adjusted across these terminal layout and pane lifecycle paths.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is just the template headings and contains no actual summary, testing, review, security, or notes content. Replace the template with filled-out Summary, Screenshots, Testing, AI Review Report, Security Audit, and Notes sections.
Docstring Coverage ⚠️ Warning Docstring coverage is 3.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main changes: terminal history, focus recovery, and stale PTY event handling.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/renderer/src/components/terminal-pane/pty-transport.ts (1)

509-521: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add the missing why for stale PTY data gating.

The new ptyId !== id guards encode the late-callback/rebind invariant; add one short Why: comment so the replay and live-data paths don’t look like redundant defensive checks.

Suggested comment
     // Why: relay pty.attach sends replay data via a dedicated pty:replay IPC
     // channel. Route it through onReplayData so the renderer engages the
     // replay guard and xterm auto-replies do not leak into the shell.
+    // Why: callbacks from an old PTY can arrive after this transport rebinds;
+    // ignore them instead of delivering stale output into the current pane.
     ptyReplayHandlers.set(id, (data) => {

As per coding guidelines, “When writing or modifying code driven by a design doc or non-obvious constraint, add a comment explaining why the code behaves the way it does.”

Source: Coding guidelines

src/renderer/src/components/terminal-pane/remote-runtime-pty-transport.ts (1)

356-358: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the remote subscription identity guard.

The captured handle/PTY pair prevents late callbacks from stale streams mutating current state; add a short Why: comment near isCurrentSubscription.

Suggested comment
     const subscribedHandle = handle
     const subscribedPtyId = remotePtyId
+    // Why: stream callbacks can arrive after attach/reconnect switches handles;
+    // only the subscription for the current handle/PTY may mutate state.
     const isCurrentSubscription = (): boolean =>
       isCurrentRemoteTerminal(subscribedHandle, subscribedPtyId)

As per coding guidelines, “When writing or modifying code driven by a design doc or non-obvious constraint, add a comment explaining why the code behaves the way it does.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f4482e74-02c6-4193-a1f5-eee9fd83d021

📥 Commits

Reviewing files that changed from the base of the PR and between f6ad569 and 2bc8e16.

📒 Files selected for processing (18)
  • src/main/runtime/orca-runtime.test.ts
  • src/main/runtime/orca-runtime.ts
  • src/renderer/src/components/terminal-pane/TerminalPane.tsx
  • src/renderer/src/components/terminal-pane/pty-connection-types.ts
  • src/renderer/src/components/terminal-pane/pty-connection.test.ts
  • src/renderer/src/components/terminal-pane/pty-connection.ts
  • src/renderer/src/components/terminal-pane/pty-transport.test.ts
  • src/renderer/src/components/terminal-pane/pty-transport.ts
  • src/renderer/src/components/terminal-pane/remote-runtime-pty-transport.test.ts
  • src/renderer/src/components/terminal-pane/remote-runtime-pty-transport.ts
  • src/renderer/src/components/terminal-pane/terminal-layout-leaf-ids.test.ts
  • src/renderer/src/components/terminal-pane/terminal-layout-leaf-ids.ts
  • src/renderer/src/components/terminal-pane/terminal-shutdown-layout-capture.test.ts
  • src/renderer/src/components/terminal-pane/terminal-shutdown-layout-capture.ts
  • src/renderer/src/components/terminal-pane/use-terminal-pane-lifecycle.ts
  • src/renderer/src/hooks/useIpcEvents.ts
  • src/renderer/src/store/slices/terminals-hydration.test.ts
  • src/renderer/src/store/slices/terminals.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/renderer/src/components/terminal-pane/pty-connection.ts (1)

1321-1328: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Clear the stale layout binding for obsolete transport exits.

This branch removes tab ownership but leaves the pane’s persisted PTY binding untouched. If the replacement transport has not spawned/synced yet, the exited ptyId can remain in terminalLayoutsByTabId and be reused by shutdown/hydration. Use the same guarded helper as the normal exit and reattach-expiration paths.

🐛 Proposed fix
     if (currentPaneTransport && currentPaneTransport !== transport) {
       // Why: an old transport can deliver a late exit after this pane has
       // rebound to a replacement PTY; only clear ownership for the exited id.
+      deps.clearExitedPanePtyLayoutBinding(pane.id, ptyId)
       deps.clearTabPtyId(deps.tabId, ptyId)
       deps.consumeSuppressedPtyExit(ptyId)
       scheduleRuntimeGraphSync()
       return
     }
src/renderer/src/components/terminal-pane/TerminalPane.tsx (2)

1167-1173: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Move active-pane restoration ahead of the insertion fast-path and before persisting.

This block still sits after the insertions.length === 0 early return, so a host snapshot that only changes activeLeafId never updates the manager’s active pane. And when insertions do happen, persistLayoutSnapshot() runs first, so the store can immediately reserialize the old active leaf.

Suggested fix
-    if (insertions.length === 0) {
-      return
-    }
-
     let appliedInsertion = false
     for (const insertion of insertions) {
       // ...
     }
-
-    if (appliedInsertion) {
-      persistLayoutSnapshot()
-    }
 
     const activePaneId = restoredLayout.activeLeafId
       ? manager.getNumericIdForLeaf(restoredLayout.activeLeafId)
       : null
     const fallbackActivePaneId = manager.getActivePane()?.id ?? manager.getPanes()[0]?.id ?? null
     const nextActivePaneId = activePaneId ?? fallbackActivePaneId
     if (nextActivePaneId !== null) {
       manager.setActivePane(nextActivePaneId, { focus: isActive })
     }
+    if (appliedInsertion) {
+      persistLayoutSnapshot()
+    }

1317-1336: 📐 Maintainability & Code Quality | 🟡 Minor

Remove duplicate clearExitedPanePtyLayoutBinding from dependency array

clearExitedPanePtyLayoutBinding appears twice in the dependency array within src/renderer/src/components/terminal-pane/TerminalPane.tsx. This redundancy adds no value and can trigger exhaustive-deps warnings or linter noise.

Remove the duplicate entry to maintain clean, idiomatic code.

View snippet
[
  clearCodexRestartNotice,
  clearExitedPanePtyLayoutBinding,
  clearRuntimePaneTitle,
  clearTabPtyId,
  cwd,
  dispatchNotification,
  markWorktreeUnread,
  markTerminalTabUnread,
  markTerminalPaneUnread,
  clearWorktreeUnread,
  clearTerminalTabUnread,
  clearTerminalPaneUnread,
  showRestoredSessionBanner,
  onPtyExitRef,
  setCacheTimerStartedAt,
  setRuntimePaneTitle,
  suppressPtyExit,
  clearExitedPanePtyLayoutBinding,
  syncPanePtyLayoutBinding,
]

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 96578cb5-7919-4140-9eb2-cdf73307b98d

📥 Commits

Reviewing files that changed from the base of the PR and between 2bc8e16 and 4d566f6.

📒 Files selected for processing (10)
  • src/main/runtime/orca-runtime.test.ts
  • src/main/runtime/orca-runtime.ts
  • src/renderer/src/components/terminal-pane/TerminalPane.tsx
  • src/renderer/src/components/terminal-pane/pty-connection.test.ts
  • src/renderer/src/components/terminal-pane/pty-connection.ts
  • src/renderer/src/components/terminal-pane/terminal-layout-leaf-ids.test.ts
  • src/renderer/src/components/terminal-pane/terminal-layout-leaf-ids.ts
  • src/renderer/src/components/terminal-pane/terminal-shutdown-layout-capture.test.ts
  • src/renderer/src/components/terminal-pane/terminal-shutdown-layout-capture.ts
  • src/renderer/src/hooks/useIpcEvents.ts
✅ Files skipped from review due to trivial changes (2)
  • src/renderer/src/components/terminal-pane/terminal-shutdown-layout-capture.ts
  • src/renderer/src/hooks/useIpcEvents.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/runtime/orca-runtime.ts
  • src/main/runtime/orca-runtime.test.ts
  • src/renderer/src/components/terminal-pane/pty-connection.test.ts

* Unify terminal-driven worktree activation and Cmd+J recency marking.
* Record worktree visits in the back/forward history stack unless navigating history.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0c71a563-f076-430b-9b39-b23cfa315c6b

📥 Commits

Reviewing files that changed from the base of the PR and between cd0a2dc and 1185580.

📒 Files selected for processing (6)
  • src/renderer/src/components/terminal-pane/TerminalPane.tsx
  • src/renderer/src/components/terminal-pane/terminal-layout-leaf-ids.ts
  • src/renderer/src/components/terminal-pane/terminal-visibility-resume.ts
  • src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.test.ts
  • src/renderer/src/components/terminal-pane/use-terminal-pane-global-effects.ts
  • src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.ts
💤 Files with no reviewable changes (1)
  • src/renderer/src/components/terminal-pane/TerminalPane.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/src/components/terminal-pane/terminal-layout-leaf-ids.ts

- Repair the active terminal pane leaf selection when a pane's PTY exits, preventing focus from being stranded on a dead pane.
- Ensure stale, pruned, or unbound terminal leaves are not persisted as active in visual layouts or shutdown snapshots.
- Ignore late exit notifications and subscription end events from old PTY transports or remote stream handles after a reconnect or rebind.
- Redirect active keyboard focus to a live, PTY-backed sibling pane
  if the focused pane dies, fails to spawn, or is hydrated without a
  live connection.
- Ignore stale data, replay messages, and subscription rejection errors
  from older PTY sessions after a transport has reconnected.
- Avoid persisting stale PTY bindings and dead pane focus inside
  terminal shutdown layout snapshots.
When switching between different remote terminal handles, debounced input
meant for the previous terminal could get incorrectly flushed and sent
to the newly attached terminal.

Fix this by clearing the batcher's pending state (text and byte counts)
and invoking `inputBatcher.clear()` if the attached terminal handle
changes.
- Extract and enhance window focus and visibility change recovery logic
  into a dedicated `useTerminalWindowWakeRecovery` hook.
- Recover the xterm renderer, input surface, and WebGL texture atlases
  when macOS/display wakes or the window/tab regains focus.
- Schedule recovery steps with requestAnimationFrame to ensure the
  terminal layout settles correctly.
@AmethystLiang AmethystLiang force-pushed the wire-terminal-worktree-navitgation-to-back-and-forth-stack branch from 1185580 to 7e3ba42 Compare June 28, 2026 05:34
Avoid mutating a local `let` variable from inside a Promise executor
closure by wrapping the rejection callback in a `const` object.
When window focus and visibility events fire concurrently, they can trigger multiple redundant wake recovery passes.

- Return early if a wake recovery frame is already scheduled.
- Keep the scheduled animation frame instead of canceling it to ensure we get a settled recovery pass.
…minal-worktree-navitgation-to-back-and-forth-stack
@AmethystLiang AmethystLiang merged commit 3c614b2 into main Jun 29, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant